Daily Coding Problem 4
This problem was asked by Stripe.
Given an array of integers, find the first missing positive integer in linear time and constant space. In other words, find the lowest positive integer that does not exist in the array. The array can contain duplicates and negative numbers as well.
For example, the input [3, 4, -1, 1] should give 2. The input [1, 2, 0] should give 3.
You can modify the input array in-place.
Solution overview
Constraints and assumptions
0is not considered a positive integer. In the first example, the answer is expected to be2. If0were considered a positive integer, the answer would have been0.-
Linear time complexity, also known as
O(n). To quote Wikipedia... there is a constant
csuch that the running time is at mostcnfor every input of sizen. -
Constant space complexity, also known as
O(1). To quote StackOverflow...
O(1)denotes constant (10, 100 and 1000 are constant) space and DOES NOT vary depending on the input size (sayN).
What do these constraints mean?
Linear time complexity
Linear time complexity limits the algorithms that can be used to solve this problem. For example, a comparison-based sort cannot perform better than O(n log n) in the worst case, which means that we cannot use any comparison-based sorting algorithm to solve this problem.
Having said that, there are non-comparison sorting algorithms that, at least on paper, can have a linear worst-case time complexity.
Constant space complexity
This means that we cannot change the space used by the algorithm as a function of the input size. Or, in other words, I can't simply create a min-heap, for example, and ask for the minimum value because the heap's size will change each time depending on the size of the input array. In case someone is thinking about creating a massive data structure to use for each execution of the algorithm (e.g. always create an Array of size MAX_INT for execution, regardless of the size of the input array) - while I guess that this is theoretically constant space, we run into the following problems:
- This is a hugely wasteful approach to solving the problem.
- This solution will fail if we can use a data structure larger than addressable memory, for example a data structure that can be stored on disk.
- I don't believe this is in the spirit of the problem.
On a side note, the final solution will not be pretty because:
- Most of F#'s data structures, by default, are immutable. Luckily, Arrays are not.
- F# encourages a functional programming style and, I believe, the final code will have to be more procedural.
So, how do we solve it?
I thought of a lot of solutions to solve this problem that can meet the given constraints.
-
Construct a min-heap / binary tree-style data structure and find the root (while filtering out negative values). Violates space complexity
After creating the heap / tree, filter the values to remove those entries where
value + 1is also in the structure.
-
Use 2 sets and perform a set difference. Violates space complexity
- Create a set using the original values (call it
Set(orig)) - Create a set of original values + 1 (call it
Set(plusone)) Do
Set(plusone) - Set(orig)and find the smallest positive integer
- Create a set using the original values (call it
-
Sort the array and find the first entry where the following entry's value is not
current entry + 1. Violates space and time complexity-
In other words, using a 0-indexed array:
1: 2: 3:
for i in 0 .. array.length - 2 do if array[i] + 1 <> array[i + 1] then return array[i] + 1 -
This can work if I can use a linear time and constant space sort. As far as I know, there is no such sort.
- Comparison sorts violate time constraint
- Non-comparison sorts violate space constraint
-
-
Recognize that if an array has length
n, the answer can be, at most,n + 1.-
Consider an array that is 1-indexed.
- If all the values in the array are
0or negative, the answer is1. - If all the values in the array are
>= 2, the answer is1. - If the value at each index of the array is the same as the index, the answer is
n + 1.
- If all the values in the array are
- While I haven't done anything close to a formal proof for this, I believe I am right about this.
- It took me a long time to arrive at this. I had been working on this problem in my head for a couple of weeks (mostly while commuting to and from work) before I came to this realization. I also had to force myself to think procedurally, like in C/C++, to get to this answer.
-
Consider an array that is 1-indexed.
Code
The following is the solution I came up with.
Strategy for the code
At a high level, the code adopts the following strategy:
-
First pass. Places values in their corresponding indices.
1: 2: 3:
for each entry do while the entry's value is not 0 and is not index + 1 do swap values with the index represented by the value in the entry -
Second pass. Find the first entry where the value is
0, returnindex + 1.1: 2: 3:
Scan the array for the first entry with value of 0 If found, return index + 1 Else, return array length
When reading the code, there are two items to remember. These may seem obvious, but I had to keep reminding myself courtesy of 0-based arrays in a situation where 0 has special meaning:
- Given an
index, the value there should beindex + 1, i.e.value = index + 1 - Given a
value, the index it belongs to should bevalue - 1, i.e.index = value - 1
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: |
|
Alternate solutions
In order to test this solution, we need to write a few alternate solutions to ensure that the results are correct.
I suggested three solutions above, and have implemented two of them below.
Set-based solution
The first alternate solution solves the problem by creating two sets and performing set subtraction. Sets will automatically remove duplicate values, so I do not need to handle that situation explicitly.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: |
|
Sort-based solution
In the second alternate solution, I solve the problem by sorting the array and finding the first pair of values that is not consecutive. Duplicate values are explicitly removed to avoid issues with comparison later.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: |
|
Utility functions
Some utility functions to work with 3-tuples and to collect performance data using Windows Performance Counters.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: |
|
Testing
Base tests
Considering the "index math" happening in these solutions, we need to thoroughly test them to find any issues. The first tests are to ensure that the two given test cases work correctly for each of the algorithms.
After much struggling with FsCheck, I'm happy to start using a property-based testing tool that works with Jupyter and FSharp.Literate, Hedgehog.
I am going to use a stopwatch to help measure runtimes since I am using FSharp.Literate instead of Jupyter for this post, and I can't figure out how to enable the #time directive through FSharp.Literate.
1: 2: 3: 4: 5: 6: |
|
First, let's test the main solver.
1: 2: 3: 4: 5: |
|
|
1: 2: 3: 4: 5: |
|
|
Next, we test the set-based solver.
1: 2: 3: 4: 5: |
|
|
1: 2: 3: 4: 5: |
|
|
Finally, we can test the sort-based solver.
1: 2: 3: 4: 5: |
|
|
1: 2: 3: 4: 5: |
|
|
Additional property-based tests between the algorithms.
Now that the basic smoke tests are out of the way, we can move on to some more serious property-based testing.
The first test will exercise all three algorithms over the full range of integers.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: |
|
|
The second test tries various combinations of positive integers over larger arrays.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: |
|
|
The third test ensures that 1-element arrays are handled correctly.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: |
|
|
And a final edge-case test with empty arrays to ensure that the algorithms are working correctly.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: |
|
|
And the runtime for the base tests is given below.
1:
|
|
1: 2: 3: 4: |
|
Base Test Runtime was:
|
Performance Testing
Now that the algorithms seem to be working correctly, the next step is to capture some performance metrics. For this portion, I will be using System.Diagnostics.Stopwatch to measure each algorithm's speed. I believe my last post proved that, as of now, I cannot benchmark memory usage reliably.
First, we can setup a generator to generate random values for testing. Similar to the base testing, let's benchmark the runtime of the test data generation.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: |
|
1: 2: 3: 4: |
|
Data Generation Runtime was:
|
Now that we have our profiling data, the next step is to measure performance.
First, let's create a bare minimum set of utility 'tools' to enable the performance monitoring.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: |
|
Solver
Let's start by benchmarking the base solver algorithm.
1:
|
|
1: 2: 3: 4: |
|
|
Set Solver
The next benchmark is with the setSolver algorithm.
1:
|
|
1: 2: 3: 4: |
|
|
Sort Solver
And finally, the benchmark for the sortSolver algorithm.
1:
|
|
1: 2: 3: 4: |
|
|
Results
So, what were the results? First, let's chart the runtimes as measured by the stopwatches (algorithm Alg, garbage collection GC, and total time TT) and some comparative percentages.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: |
|
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: |
|
1: 2: 3: 4: 5: |
|
Performance Counter statistics
While executing the workbook, I collected a number of values using Performance Counters on Windows. These were collected throughout the life of the book's execution. For descriptions of these counters, please refer to the .NET Performance Counters page.
-
.NET CLR LocksAndThreads
- # of current logical Threads
- # of current physical Threads
-
.NET CLR Memory
- # Gen 0 Collections
- # Gen 1 Collections
- # Gen 2 Collections
- Gen 0 heap size
- Gen 1 heap size
- Gen 2 heap size
- Large Object Heap size
-
Process
- Private Bytes
- % Processor Time
I thought that it would be interesting to chart these values out.
However, the first thing we need to do is to stop collecting the data.
1:
|
|
.NET CLR LocksAndThreads, # of Current Threads
1: 2: 3: |
|
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: |
|
.NET CLR Memory, # of Collections
1: 2: 3: |
|
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: |
|
.NET CLR Memory, Heap size
1: 2: 3: 4: |
|
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: |
|
Process, Private Bytes
1: 2: 3: 4: 5: 6: 7: 8: 9: |
|
Process, % Processor Time
1: 2: 3: 4: 5: 6: 7: 8: 9: |
|
Conclusion
So, I think the place to begin is with a seemingly obvious statement - time and space complexity matters and should be balanced in conjunction with other considerations such as conceptual "simplicity" and maintainability. For example, the set solver was one of the simplest solutions (for me) to understand. However, the algorithm runtime was 521x slower and the total runtime was 165x slower than the fastest solution, with the total runtimes being 4 hours 42 minutes compared to less than 2 minutes on the same dataset.
The memory statistics present an interesting story, with the number of Gen 0 collections growing at a linear rate through the life of the program. Now, most of the program's time was spent in the Set Solver's performance test. At the end, there is a sharper uptick in Gen 0 collections, possibly due to the Sort Solver's performance test. However, as I did not implement a method to correlate counter readings to the currently running code block, I can't say that with absolute certainty.
In general, I am extremely pleased with how Hedgehog was able to take on a large percentage of the burdens related to property testing and random test data generation. Windows Performance Counters were also a pleasant find and a good alternative to BenchmarkDotNet. I definitely plan to use these going forward, at least until FsLab comes to .NET 3.0 and I can use something like dotnet-counters.
1:
|
|
1: 2: |
|
And finally, the overall run-time of this notebook is...
|
Full name: Microsoft.FSharp.Core.array<_>
Full name: P4.solver
The main function.
swap the value in between 2 indices.
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
module Array
from YoLo
--------------------
module Array
from Microsoft.FSharp.Collections
Full name: Microsoft.FSharp.Collections.Array.length
Full name: Microsoft.FSharp.Core.Operators.not
First pass, put each value in its corresponding index
Second pass, find the first 0 or return (array.length + 1) as the result
Full name: Microsoft.FSharp.Collections.Array.tryFindIndex
Full name: P4.setSolver
Full name: Microsoft.FSharp.Collections.Array.filter
module Set
from Microsoft.FSharp.Collections
--------------------
--------------------
new : elements:seq<'T> -> Set<'T>
Full name: P4.sortSolver
Full name: Microsoft.FSharp.Collections.Array.sort
Full name: Microsoft.FSharp.Collections.Array.distinct
Full name: Microsoft.FSharp.Collections.Array.contains
module Seq
from YoLo
--------------------
module Seq
from Microsoft.FSharp.Collections
Full name: Microsoft.FSharp.Collections.Seq.windowed
Full name: Microsoft.FSharp.Collections.Seq.filter
Full name: Microsoft.FSharp.Collections.Seq.tryHead
Full name: P4.mfst
Get first value from 3-tuple.
Full name: P4.msnd
Full name: P4.mtrd
Full name: P4.counterNames
A list of all counter categories and names we want to track
Full name: P4.counterResults
Store readings from the counters
Full name: Microsoft.FSharp.Collections.ResizeArray<_>
val float32 : value:'T -> float32 (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.float32
--------------------
type float32 = Single
Full name: Microsoft.FSharp.Core.float32
--------------------
type float32<'Measure> = float32
Full name: Microsoft.FSharp.Core.float32<_>
member Clone : unit -> obj
member CopyTo : array:Array * index:int -> unit + 1 overload
member GetEnumerator : unit -> IEnumerator
member GetLength : dimension:int -> int
member GetLongLength : dimension:int -> int64
member GetLowerBound : dimension:int -> int
member GetUpperBound : dimension:int -> int
member GetValue : [<ParamArray>] indices:int[] -> obj + 7 overloads
member Initialize : unit -> unit
member IsFixedSize : bool
...
Full name: System.Array
Full name: Microsoft.FSharp.Collections.Array.init
module List
from YoLo
--------------------
module List
from Microsoft.FSharp.Collections
--------------------
Full name: P4.cancelToken
Cancellation token so that we can stop collection
type CancellationTokenSource =
new : unit -> CancellationTokenSource
member Cancel : unit -> unit + 1 overload
member Dispose : unit -> unit
member IsCancellationRequested : bool
member Token : CancellationToken
static member CreateLinkedTokenSource : [<ParamArray>] tokens:CancellationToken[] -> CancellationTokenSource + 1 overload
Full name: System.Threading.CancellationTokenSource
--------------------
CancellationTokenSource() : unit
Full name: P4.counters
Async process to continuously collect the data, with 1 second sleep
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
type PerformanceCounter =
inherit Component
new : unit -> PerformanceCounter + 5 overloads
member BeginInit : unit -> unit
member CategoryName : string with get, set
member Close : unit -> unit
member CounterHelp : string
member CounterName : string with get, set
member CounterType : PerformanceCounterType
member Decrement : unit -> int64
member EndInit : unit -> unit
member Increment : unit -> int64
...
Full name: System.Diagnostics.PerformanceCounter
--------------------
PerformanceCounter() : unit
PerformanceCounter(categoryName: string, counterName: string) : unit
PerformanceCounter(categoryName: string, counterName: string, instanceName: string) : unit
PerformanceCounter(categoryName: string, counterName: string, readOnly: bool) : unit
PerformanceCounter(categoryName: string, counterName: string, instanceName: string, machineName: string) : unit
PerformanceCounter(categoryName: string, counterName: string, instanceName: string, readOnly: bool) : unit
type Process =
inherit Component
new : unit -> Process
member BasePriority : int
member BeginErrorReadLine : unit -> unit
member BeginOutputReadLine : unit -> unit
member CancelErrorRead : unit -> unit
member CancelOutputRead : unit -> unit
member Close : unit -> unit
member CloseMainWindow : unit -> bool
member EnableRaisingEvents : bool with get, set
member ExitCode : int
...
Full name: System.Diagnostics.Process
--------------------
Process() : unit
type Thread =
inherit CriticalFinalizerObject
new : start:ThreadStart -> Thread + 3 overloads
member Abort : unit -> unit + 1 overload
member ApartmentState : ApartmentState with get, set
member CurrentCulture : CultureInfo with get, set
member CurrentUICulture : CultureInfo with get, set
member DisableComObjectEagerCleanup : unit -> unit
member ExecutionContext : ExecutionContext
member GetApartmentState : unit -> ApartmentState
member GetCompressedStack : unit -> CompressedStack
member GetHashCode : unit -> int
...
Full name: System.Threading.Thread
--------------------
Thread(start: ThreadStart) : unit
Thread(start: ParameterizedThreadStart) : unit
Thread(start: ThreadStart, maxStackSize: int) : unit
Thread(start: ParameterizedThreadStart, maxStackSize: int) : unit
Thread.Sleep(millisecondsTimeout: int) : unit
module Async
from YoLo
--------------------
--------------------
type Async<'T>
Full name: Microsoft.FSharp.Control.Async<_>
Full name: P4.overallStopwatch
A stopwatch to measure the overall program's run-time.
type Stopwatch =
new : unit -> Stopwatch
member Elapsed : TimeSpan
member ElapsedMilliseconds : int64
member ElapsedTicks : int64
member IsRunning : bool
member Reset : unit -> unit
member Restart : unit -> unit
member Start : unit -> unit
member Stop : unit -> unit
static val Frequency : int64
...
Full name: System.Diagnostics.Stopwatch
--------------------
Stopwatch() : unit
Full name: P4.stopWatch
The stopwatch that will measure run-times for the rest of the code.
Full name: Hedgehog.PropertyBuilder.property
union case Property.Property: Gen<Journal * Result<'a>> -> Property<'a>
--------------------
module Property
from Hedgehog
--------------------
type Property<'a> = | Property of Gen<Journal * Result<'a>>
Full name: Hedgehog.Property<_>
Full name: Hedgehog.Property.print'
type tests
Full name: Hedgehog.tests
union case Gen.Gen: Random<Tree<'a>> -> Gen<'a>
--------------------
module Gen
from Hedgehog
--------------------
type Gen<'a> = | Gen of Random<Tree<'a>>
Full name: Hedgehog.Gen<_>
Full name: Hedgehog.Gen.array
union case Range.Range: ('a * (Size -> 'a * 'a)) -> Range<'a>
--------------------
module Range
from Hedgehog
--------------------
type Range<'a> = | Range of ('a * (Size -> 'a * 'a))
Full name: Hedgehog.Range<_>
Full name: Hedgehog.Range.exponential
Full name: Hedgehog.Gen.int
Full name: Hedgehog.Range.constant
struct
member CompareTo : value:obj -> int + 1 overload
member Equals : obj:obj -> bool + 1 overload
member GetHashCode : unit -> int
member GetTypeCode : unit -> TypeCode
member ToString : unit -> string + 3 overloads
static val MaxValue : int
static val MinValue : int
static member Parse : s:string -> int + 3 overloads
static member TryParse : s:string * result:int -> bool + 1 overload
end
Full name: System.Int32
type TimeSpan =
struct
new : ticks:int64 -> TimeSpan + 3 overloads
member Add : ts:TimeSpan -> TimeSpan
member CompareTo : value:obj -> int + 1 overload
member Days : int
member Duration : unit -> TimeSpan
member Equals : value:obj -> bool + 1 overload
member GetHashCode : unit -> int
member Hours : int
member Milliseconds : int
member Minutes : int
...
end
Full name: System.TimeSpan
--------------------
TimeSpan()
TimeSpan(ticks: int64) : unit
TimeSpan(hours: int, minutes: int, seconds: int) : unit
TimeSpan(days: int, hours: int, minutes: int, seconds: int) : unit
TimeSpan(days: int, hours: int, minutes: int, seconds: int, milliseconds: int) : unit
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
Full name: P4.arrayGen
Full name: Hedgehog.GenBuilder.gen
Full name: P4.numX
Full name: P4.numY
Full name: P4.tc
Full name: Hedgehog.Gen.sample
Full name: Hedgehog.Size
Full name: Microsoft.FSharp.Collections.Array.fold
Full name: Microsoft.FSharp.Collections.Array.append
Full name: P4.numRuns
The number of runs to perform, so average the performance.
Full name: P4.measurement
Function that helps measure the average runtime based on the number of runs
static member AddMemoryPressure : bytesAllocated:int64 -> unit
static member CancelFullGCNotification : unit -> unit
static member Collect : unit -> unit + 2 overloads
static member CollectionCount : generation:int -> int
static member GetGeneration : obj:obj -> int + 1 overload
static member GetTotalMemory : forceFullCollection:bool -> int64
static member KeepAlive : obj:obj -> unit
static member MaxGeneration : int
static member ReRegisterForFinalize : obj:obj -> unit
static member RegisterForFullGCNotification : maxGenerationThreshold:int * largeObjectHeapThreshold:int -> unit
...
Full name: System.GC
GC.Collect(generation: int) : unit
GC.Collect(generation: int, mode: GCCollectionMode) : unit
| Default = 0
| Forced = 1
| Optimized = 2
Full name: System.GCCollectionMode
Full name: Microsoft.FSharp.Core.Operators.ignore
val int64 : value:'T -> int64 (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int64
--------------------
type int64 = Int64
Full name: Microsoft.FSharp.Core.int64
--------------------
type int64<'Measure> = int64
Full name: Microsoft.FSharp.Core.int64<_>
Full name: P4.solverRuntime
Full name: P4.setSolverRuntime
Full name: P4.sortSolverRuntime
Full name: P4.rows
Full name: P4.columns
Full name: P4.algRes
Full name: P4.gcRes
Full name: P4.ttRes
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
static member Annotation : data:seq<#seq<DateTime * 'V * string * string>> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires 'V :> value)
static member Annotation : data:seq<DateTime * #value * string * string> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
static member Area : data:seq<#seq<'K * 'V>> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires 'K :> key and 'V :> value)
static member Area : data:seq<#key * #value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
static member Area : data:seq<#value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
static member Bar : data:seq<#seq<'K * 'V>> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires 'K :> key and 'V :> value)
static member Bar : data:seq<#key * #value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
static member Bar : data:seq<#value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
static member Bubble : data:seq<string * #value * #value * #value * #value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
static member Bubble : data:seq<string * #value * #value * #value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
...
Full name: XPlot.GoogleCharts.Chart
static member Chart.Table : data:Deedle.Series<'K,#value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires equality and 'K :> key)
static member Chart.Table : data:seq<Deedle.Series<'K,#value>> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires equality and 'K :> key)
static member Chart.Table : data:seq<#seq<'K * 'V>> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires 'K :> key and 'V :> value)
static member Chart.Table : data:seq<#key * #value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
type Options =
new : unit -> Options
member ShouldSerializeaggregationTarget : unit -> bool
member ShouldSerializeallValuesSuffix : unit -> bool
member ShouldSerializeallowHtml : unit -> bool
member ShouldSerializealternatingRowStyle : unit -> bool
member ShouldSerializeanimation : unit -> bool
member ShouldSerializeannotations : unit -> bool
member ShouldSerializeannotationsWidth : unit -> bool
member ShouldSerializeareaOpacity : unit -> bool
member ShouldSerializeavoidOverlappingGridLines : unit -> bool
...
Full name: XPlot.GoogleCharts.Configuration.Options
--------------------
new : unit -> Options
static member Chart.Bar : data:Deedle.Series<'K,#value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires equality and 'K :> key)
static member Chart.Bar : data:seq<Deedle.Series<'K,#value>> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires equality and 'K :> key)
static member Chart.Bar : data:seq<#seq<'K * 'V>> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires 'K :> key and 'V :> value)
static member Chart.Bar : data:seq<#key * #value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
static member Chart.Bar : data:seq<#value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
CancellationTokenSource.Cancel(throwOnFirstException: bool) : unit
Full name: P4.minThreadSize
static member Chart.Line : data:Deedle.Series<'K,#value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires equality and 'K :> key)
static member Chart.Line : data:seq<Deedle.Series<'K,#value>> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires equality and 'K :> key)
static member Chart.Line : data:seq<#seq<'K * 'V>> * ?Labels:seq<string> * ?Options:Options -> GoogleChart (requires 'K :> key and 'V :> value)
static member Chart.Line : data:seq<#key * #value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
static member Chart.Line : data:seq<#value> * ?Labels:seq<string> * ?Options:Options -> GoogleChart
type Legend =
new : unit -> Legend
member ShouldSerializealignment : unit -> bool
member ShouldSerializemaxLines : unit -> bool
member ShouldSerializenumberFormat : unit -> bool
member ShouldSerializeposition : unit -> bool
member ShouldSerializetextStyle : unit -> bool
member alignment : string
member maxLines : int
member numberFormat : string
member position : string
...
Full name: XPlot.GoogleCharts.Configuration.Legend
--------------------
new : unit -> Legend
Full name: P4.minCollectionSize
Full name: P4.minHeapSize